package com.ds.lens.data.service.metric;

import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectQuery;
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.ds.lens.data.common.constant.AggregatorEnum;
import com.ds.lens.data.common.query.MQLVariable;
import com.ds.lens.data.common.query.QueryAdaptor;
import com.ds.lens.data.common.umpapi.MatrixMetric;
import com.ds.lens.data.common.umpapi.MatrixMetricOutBO;
import com.ds.lens.data.common.umpapi.UmpMetricClient;
import com.ds.lens.data.common.umpapi.UmpMetricOutVO;
import com.ds.lens.data.common.util.HttpServiceClient;
import com.ds.lens.data.common.util.MqlParseUtil;
import com.ds.lens.data.common.util.Pair;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * Description:
 *
 * @author WeiShaoying
 * @date 2019-12-24
 */
public class UmpQueryAdaptor implements QueryAdaptor<List<Map<String, Object>>> {

    private UmpMetricClient umpMetricClient;

    public UmpQueryAdaptor(String url) {
        umpMetricClient = HttpServiceClient.getRestClient(UmpMetricClient.class, url);
    }

    @Override
    public void connect() throws Exception {

    }

    @Override
    public List<Map<String, Object>> query(String sql, MQLVariable... variables) throws Exception {
        SQLStatement statement = null;
        try {
            statement = SQLUtils.parseSingleStatement(sql, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (statement instanceof SQLSelectStatement) {
            SQLSelectQuery sqlSelectQuery = ((SQLSelectStatement) statement).getSelect().getQuery();
            if (sqlSelectQuery instanceof SQLSelectQueryBlock) {
                try {
                    SQLSelectQueryBlock selectQueryBlock = (SQLSelectQueryBlock) sqlSelectQuery;
                    Pair<String, AggregatorEnum> metric = new Pair<>(null, null);
                    MqlParseUtil.parseSelectItems(metric, selectQueryBlock.getSelectList(), selectQueryBlock.getGroupBy());

                    Pair<Long, Long> timeRange = new Pair<>(null, null);
                    Map<String, List<Object>> conditionMap = new HashMap<>();
                    MqlParseUtil.parseConditions(conditionMap, timeRange, selectQueryBlock.getWhere());

                    Pair<String, Long> interval = new Pair<>(null, null);
                    List<String> groups = new ArrayList<>();
                    MqlParseUtil.parseGroupBy(groups, interval, selectQueryBlock.getGroupBy());

                    Pair<Integer, Integer> groupLimit = new Pair<>(null, null);
                    MqlParseUtil.parseLimit(groupLimit, selectQueryBlock.getLimit());

                    Long endTime = timeRange.getRight() - interval.getRight();
                    double start = timeRange.getLeft() / 1000D;
                    double end = endTime / 1000D;
                    long step = interval.getRight() / 1000;
                    String queryString = getQueryString(metric, conditionMap, groups);
                    return queryMetrics(metric, groups, start, end, step, queryString, groupLimit);
                } catch (Exception e) {
                    throw new RuntimeException(sql + " cannot convert to UMP query object", e);
                }
            }
        }
        return Collections.emptyList();
    }

    private List<Map<String, Object>> queryMetrics(Pair<String, AggregatorEnum> metric, List<String> groups, double start, double end, long step, String queryString, Pair<Integer, Integer> groupLimit) throws Exception {
        UmpMetricOutVO<MatrixMetricOutBO> outVO = umpMetricClient.getMetricsByCondition(queryString, start, end, step);
        if (null == outVO) {
            return Collections.emptyList();
        }
        if (null == outVO.getData()) {
            return Collections.emptyList();
        }
        List<MatrixMetric> result = outVO.getData().getResult();
        if (CollectionUtils.isEmpty(result)) {
            return Collections.emptyList();
        }
        if (null != groupLimit && null != groupLimit.getRight()) {
            if (result.size() > groupLimit.getRight()) {
                result = result.subList(0, groupLimit.getRight());
            }
        }
        List<Map<String, Object>> resultList = new ArrayList<>();
        for (MatrixMetric matrixMetric : result) {
            for (List<Object> metricValue : matrixMetric.getValues()) {
                Map<String, Object> map = new HashMap<>();
                if (metricValue.size() > 1) {
                    BigDecimal timeStamp = new BigDecimal(String.valueOf(metricValue.get(0))).multiply(new BigDecimal(1000));
                    map.put("timeStamp", timeStamp.longValue());
                    BigDecimal metricVal = new BigDecimal(String.valueOf(metricValue.get(1)));
                    map.put(metric.getLeft() + "-" + metric.getRight().name(), metricVal.doubleValue());
                    if (!CollectionUtils.isEmpty(groups)) {
                        for (String group : groups) {
                            map.put(group, matrixMetric.getMetric().get(group));
                        }
                    }
                    resultList.add(map);
                }
            }
        }
        return resultList;
    }

    private static String getQueryString(Pair<String, AggregatorEnum> metric, Map<String, List<Object>> conditionMap, List<String> groups) {
        StringBuilder queryString = new StringBuilder(metric.getRight().name());
        queryString.append("(").append(metric.getLeft());
        if (!CollectionUtils.isEmpty(conditionMap)) {
            queryString.append("{");
            for (Map.Entry<String, List<Object>> entry : conditionMap.entrySet()) {
                queryString.append(entry.getKey());
                if (entry.getValue().size() > 1) {
                    queryString.append("=~\"");
                    for (Object obj : entry.getValue()) {
                        queryString.append(obj).append("|");
                    }
                    queryString.deleteCharAt(queryString.length() - 1).append("\"");
                } else {
                    queryString.append("=\"").append(entry.getValue().get(0)).append("\"");
                }
                queryString.append(",");
            }
            queryString.deleteCharAt(queryString.length() - 1).append("}");
        }
        queryString.append(")");
        if (!CollectionUtils.isEmpty(groups)) {
            queryString.append(" by (");
            for (String group : groups) {
                queryString.append(group).append(",");
            }
            queryString.deleteCharAt(queryString.length() - 1).append(")");
        }
        return queryString.toString();
    }

    public static void main(String[] args) {
//        String sql = "select AVG(test_lyw), env from 3 where cluster='hll' and env = 'lyw_mac_pro4' and ${query_timestamp} < datetime('m') and ${query_timestamp} >= datetime('-30m') group by env,cluster, date_histogram(${interval_timestamp}, '10m')";

//        SQLStatement statement = SQLUtils.parseSingleStatement(sql, null);
//        if (statement instanceof SQLSelectStatement) {
//            SQLSelectQuery sqlSelectQuery = ((SQLSelectStatement) statement).getSelect().getQuery();
//            if (sqlSelectQuery instanceof SQLSelectQueryBlock) {
//
//                try {
//                    Pair<String, AggregatorEnum> metric = new Pair<>(null, null);
//                    SQLSelectQueryBlock selectQueryBlock = (SQLSelectQueryBlock) sqlSelectQuery;
//                    MqlParseUtil.parseSelectItems(metric, selectQueryBlock.getSelectList(), selectQueryBlock.getGroupBy());
//
//                    Map<String, List<Object>> conditionMap = new HashMap<>();
//                    Pair<Long, Long> timeRange = new Pair<>(null, null);
//                    MqlParseUtil.parseConditions(conditionMap, timeRange, selectQueryBlock.getWhere());
//
//                    Pair<String, Long> interval = new Pair<>(null, null);
//                    List<String> groups = new ArrayList<>();
//                    MqlParseUtil.parseGroupBy(groups, interval, selectQueryBlock.getGroupBy());
//
//
//                    System.out.println(getQueryString(metric, conditionMap, groups));
//                } catch (Exception e) {
//                    throw new RuntimeException(sql + " cannot convert to apm query object", e);
//                }
//
//            }
//        }

    }

    @Override
    public void close() throws Exception {

    }
}
